/*
 * License GNU LGPL
 * Copyright (C) 2012 Amrullah <amrullah@panemu.com>.
 */
package com.abc.cheque.ui.table;


import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.Callback;
import javafx.util.StringConverter;

import com.abc.cheque.common.TableCriteria.Operator;

/**
 *
 * @author Amrullah <amrullah@panemu.com>
 */
public class ComboBoxColumn<S, T> extends BaseColumn<S, T> {

    private Map<String, T> itemMap = new LinkedHashMap<>();
    private ComboBox<T> searchInputControl = new ComboBox<>();
    private SearchMenuItemBase<T> searchMenuItem = new SearchMenuItemBase<T>(this) {
        @Override
        protected Node getInputControl() {
            return searchInputControl;
        }

        @Override
        protected List<Operator> getOperators() {
            List<Operator> lst = new ArrayList<>();
            lst.add(Operator.eq);
            lst.add(Operator.ne);
            lst.add(Operator.is_null);
            lst.add(Operator.is_not_null);
            return lst;
        }

        @Override
        protected T getValue() {
            return searchInputControl.getValue();
        }
    };

    public ComboBoxColumn(String propertyName) {
        this(propertyName, 100);
    }

    public ComboBoxColumn(String propertyName, double prefWidth) {
        super(propertyName, prefWidth);
        setCellFactory(new Callback<TableColumn<S, T>, TableCell<S, T>>() {
            @Override
            public TableCell<S, T> call(TableColumn<S, T> param) {
                return new ComboBoxTableCell();
            }
        });
        searchInputControl.setConverter(stringConverter);


        searchInputControl.setFocusTraversable(false);
    }

    @Override
    MenuItem getSearchMenuItem() {
        if (getDefaultSearchValue() != null) {
            searchInputControl.setValue(getDefaultSearchValue());
        }
        return searchMenuItem;
    }

    /**
     * Add pair of label and object corresponding to the label
     *
     * @param label
     * @param object
     */
    public void addItem(String label, T object) {
        itemMap.put(label, object);
        searchInputControl.getItems().add(object);
    }

    private String getLabel(T object) {
        if (object == null) {
            return "";
        }
        Set<String> keys = itemMap.keySet();
        for (String label : keys) {
            T obj = itemMap.get(label);
            if (obj.equals(object)) {
                return label;
            }
        }
        return null;
    }
    private StringConverter<T> stringConverter = new StringConverter<T>() {
        @Override
        public String toString(T object) {
            return getLabel(object);
        }

        @Override
        public T fromString(String string) {
            return itemMap.get(string);
        }
    };

    @Override
    protected T convertFromString_Impl(String stringValue) {
        T result = stringConverter.fromString(stringValue);
        return result;
    }

    @Override
    public String convertToString(T value) {
        return stringConverter.toString(value);
    }
    
    
    public class ComboBoxTableCell extends BaseCell<S, T> {

        private ComboBox<T> combobox;

        public ComboBoxTableCell() {
            super();
            this.setAlignment(ComboBoxColumn.this.getAlignment());
        }

        @Override
        protected void setValueToEditor(T value) {
            combobox.setValue(value);
        }

        @Override
        protected Control getEditor() {
            if (combobox == null) {
                combobox = new ComboBox<>();
                if (!isRequired()) {
                    combobox.getItems().add(null);
                }
                requiredProperty().addListener(new ChangeListener<Boolean>() {
                    @Override
                    public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
                        if (t1) {
                            combobox.getItems().remove(null);
                        } else {
                            combobox.getItems().add(0, null);
                        }
                    }
                });
                combobox.getItems().addAll(itemMap.values());
                combobox.setConverter(stringConverter);

                /**
                 * Disable traversing focus using LEFT and RIGHT.
                 */
                combobox.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
                    @Override
                    public void handle(KeyEvent event) {
                        if ((event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT)
                                && isEditing()) {
                            event.consume();
                        }
                    }
                });
            }
            return combobox;
        }

        @Override
        protected T getValueFromEditor() {
            return combobox.getValue();
        }

        @Override
        protected void attachEnterEscapeEventHandler() {
            /**
             * Use event filter instead on onKeyPressed because Enter and Escape
             * have been consumed by Combobox it self
             */
            combobox.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent t) {
                    if (t.getCode() == KeyCode.ENTER) {
                        commitEdit(combobox.getValue());
                        t.consume();
                    } else if (t.getCode() == KeyCode.ESCAPE) {
                        cancelEdit();
                        /**
                         * Propagate ESCAPE key press to cell
                         */
                        ComboBoxTableCell.this.fireEvent(t);
                    }
                }
            });
        }

        @Override
        protected String getString(T value) {
            return getLabel(value);
        }
    }
}
